{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a name=\"top\"></a><img src=\"source/SpinalHDL.png\" alt=\"SpinalHDL based on Scala\" style=\"width:320px;\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Before running Spinal HDL code, be sure to load SpinalHDL Libraries  \n",
    "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n",
    "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Macro\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import language.experimental.macros\n",
    "\n",
    "// Normal imports\n",
    "import reflect.macros.blackbox.Context\n",
    "\n",
    "object Step1Complete {\n",
    "  def hello(): Unit = macro hello_impl\n",
    "\n",
    "  def hello_impl(c: Context)(): c.Expr[Unit] = {\n",
    "    import c.universe._\n",
    "    println(\"Compiling!\")\n",
    "    reify { println(\"Hello World!\") }\n",
    "  }\n",
    "     \n",
    "}      "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## quasiquotes \n",
    "https://docs.scala-lang.org/overviews/quasiquotes/setup.html  \n",
    "Compared with LISP's macro"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val universe: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe\n",
    "import universe._\n",
    "val tree = q\"i am { a quasiquote }\"\n",
    "val C = q\"class C\"\n",
    "//println(showCode(C))\n",
    "//println(showCode(tree))\n",
    "//println(showRaw(tree))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val a = q\"true\"\n",
    "val b = true"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val q\"i am $what\" = q\"i am { a quasiquote }\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val ab = List(q\"a\", q\"b\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import scala.reflect.runtime.currentMirror\n",
    "import scala.tools.reflect.ToolBox\n",
    "val toolbox = currentMirror.mkToolBox()\n",
    "val fab = q\"f(..$ab)\"\n",
    "//val C = q\"class C\"\n",
    "val tree = q\"i am { a quasiquote }\"\n",
    "println(tree match { case q\"i am { a quasiquote }\" => \"it worked!\" })"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "println(q\"foo + bar\" equalsStructure q\"foo.+(bar)\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val x = q\"\"\"\n",
    "         val x: List[Int] = List(1, 2) match {\n",
    "           case List(a, b) => List(a + b)\n",
    "         }\n",
    "       \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val q\"f(..$args)\" = q\"f(a, b)\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val two = 1 + 1\n",
    "val four = q\"$two + $two\"\n",
    "val ints = List(1, 2, 3)\n",
    "val f123 = q\"f(..$ints)\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val q\"${left: Int} + ${right: Int}\" = q\"2 + 2\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val q\"..$stats\" = q\"(2,3)\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val code = q\"\"\"println(\"compiled and run at runtime!\")\"\"\"\n",
    "val compiledCode = toolbox.compile(code)\n",
    "val result = compiledCode()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val x = q\"package mypackage { class MyClass }\"\n",
    "showCode(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    " \n",
    "  def generateCode() =\n",
    "    q\"package mypackage { class MyClass }\"\n",
    "  def saveToFile(path: String, code: Tree) = {\n",
    "    val writer = new java.io.PrintWriter(path)\n",
    "    try writer.write(showCode(code))\n",
    "    finally writer.close()\n",
    "  }\n",
    "  saveToFile(\"myfile.scala\", generateCode())\n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "val q\"$expr.$tname\" = q\"a.eat\"\n",
    "val q\"${x: scala.Symbol}\" = q\"'a\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val q\"$mods def $tname[..$tparams](...$paramss): $tpt = $expr\" = q\"\"\"\n",
    "private def interrupt(namePre:String, triggers:Int*)(name:String): Unit =  \n",
    "println(\"hello scala macro\")\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val q\"..$stats0\" = q\"a; b; c\"\n",
    "val q\"..$stats1\" = q\"(a, b, c)\"\n",
    "val q\"..$stats2\" = q\"\"\"{a\n",
    "b\n",
    "c}\"\"\"\n",
    "val q\"..$stats3\" = q\"\"\"{\n",
    "val a: Int = 1\n",
    "val b: Int = 2\n",
    "val c = 3}\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    " val extractor = pq\"Foo(1, 2, 3)\"\n",
    " val extractor2 = q\"Foo(1, 2, 3)\"\n",
    "val pq\"$id(..$pats)\" = extractor\n",
    "val pq\"$id2(..$pats2)\" = extractor2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val isT =  pq\"_: T\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val f3 = q\"private implicit def f\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import language.experimental.macros\n",
    "\n",
    "// Normal imports\n",
    "import reflect.macros.blackbox.Context"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "abstract class Animal {\n",
    "  def name: String\n",
    "}\n",
    "case class Cat(name: String) extends Animal\n",
    "case class Dog(name: String) extends Animal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "object CovarianceTest extends App {\n",
    "  def printAnimalNames(animals: List[Animal]): Unit = {\n",
    "    animals.foreach { animal =>\n",
    "      println(animal.name)\n",
    "    }\n",
    "  }\n",
    "\n",
    "  val cats: List[Cat] = List(Cat(\"Whiskers\"), Cat(\"Tom\"))\n",
    "  val dogs: List[Dog] = List(Dog(\"Fido\"), Dog(\"Rex\"))\n",
    "\n",
    "  printAnimalNames(cats)\n",
    "  // Whiskers\n",
    "  // Tom\n",
    "\n",
    "  printAnimalNames(dogs)\n",
    "  // Fido\n",
    "  // Rex\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "//CovarianceTest.printAnimalNames(CovarianceTest.cats)\n",
    " val cats: List[Cat] = List(Cat(\"Whiskers\"), Cat(\"Tom\"))\n",
    "  val dogs: List[Dog] = List(Dog(\"Fido\"), Dog(\"Rex\"))\n",
    "CovarianceTest.printAnimalNames(cats)\n",
    "    CovarianceTest.printAnimalNames(dogs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "println(HTMLFactory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val htmlbody = \"html5 ${body} end\"\n",
    "val body = \"-body-\"\n",
    "StringContext(\"html5 \",\"end\").s(body)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val body = \"div xxx div \"\n",
    "val b = s\"html5 ${body} end\" \n",
    "val c = new StringContext(htmlbody).s(htmlbody)\n",
    "val d = StringContext(\"html5 ${body} end\")\n",
    " \n",
    "//val bf = \"html5 %s end\".format(body)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class T1 extends Component{\n",
    "    val f = UInt\"11000011\"\n",
    "}\n",
    "showRtl(new T1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import scala.reflect.macros.whitebox.Context\n",
    "import scala.language.experimental.macros\n",
    "import scala.annotation.StaticAnnotation\n",
    "object helloMacro {\n",
    "  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {\n",
    "    import c.universe._\n",
    "    val result = {\n",
    "      annottees.map(_.tree).toList match {\n",
    "        case ModuleDef(mods, name, Template(parents, self, body)) :: Nil =>\n",
    "          val helloMethod = DefDef(NoMods, TermName(\"hello\"), List(), List(List()), TypeTree(), Literal(Constant(\"hello\")))\n",
    "          ModuleDef(mods, name, Template(parents, self, body :+ helloMethod))\n",
    "      }\n",
    "    }\n",
    "    c.Expr[Any](result)\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import scala.reflect.macros.blackbox\n",
    "object dataMacro {\n",
    "  def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {\n",
    "    import c.universe._\n",
    "\n",
    "    val result = {\n",
    "      annottees.map(_.tree).toList match {\n",
    "        case q\"\"\"\n",
    "              class $name {\n",
    "                ..$vars\n",
    "              }\n",
    "              \"\"\" :: Nil =>\n",
    "\n",
    "          // Generate the Getter and Setter from VarDefs\n",
    "          val beanMethods = vars.collect {\n",
    "            case q\"$mods var $name: $tpt = $expr\" =>\n",
    "              val getName = TermName(\"get\" + name.encodedName.toString.capitalize)\n",
    "              val setName = TermName(\"set\" + name.encodedName.toString.capitalize)\n",
    "              println(getName)\n",
    "              val ident = Ident(name)\n",
    "              List (\n",
    "                q\"def $getName: $tpt = $ident\",\n",
    "                q\"def $setName(paramX: $tpt): Unit = { $ident = paramX }\"\n",
    "              )\n",
    "          }.flatten\n",
    "\n",
    "          // Insert the generated Getter and Setter\n",
    "          q\"\"\"\n",
    "             class $name {\n",
    "               ..$vars\n",
    "               ..$beanMethods\n",
    "             }\n",
    "           \"\"\"\n",
    "        case _ =>\n",
    "          throw new Exception(\"Macro Error\")\n",
    "      }\n",
    "    }\n",
    "    c.Expr[Any](result)\n",
    "  }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class data extends StaticAnnotation {\n",
    "  def macroTransform(annottees: Any*): Any = macro dataMacro.impl\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Scala",
   "language": "scala",
   "name": "scala"
  },
  "language_info": {
   "codemirror_mode": "text/x-scala",
   "file_extension": ".scala",
   "mimetype": "text/x-scala",
   "name": "scala",
   "nbconvert_exporter": "script",
   "version": "2.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
